package phase01.module2.code.question2;

import phase01.module2.code.question2.exceptions.InvalidLocationException;

/**
 * 棋盘类
 */
public class Board {
    private final int SIZE = 16;
    private final Pawn[][] grid;
    private int occupiedCount = 0; // 保存已落子多少

    public Board() {
        grid = new Pawn[SIZE][SIZE];
    }

    public void draw() {
        System.out.print("  ");
        for (int col = 0; col < SIZE; col++) {
            System.out.print(Integer.toString(col, 16) + " ");
        }
        System.out.println();
        for (int i = 0; i < SIZE; i++) {
            System.out.print(Integer.toString(i, 16) + " ");
            for(int j = 0;j < SIZE;j++) {
                Pawn pawn = grid[i][j];
                if(null == pawn) {
                    System.out.print("+ ");
                } else {
                    System.out.print(pawn.getLabel() + " ");
                }
            }
            System.out.println();
        }
    }

    public boolean isFullyOccupied() {
        return occupiedCount != 16 * 16;
    }

    public boolean play(Player player, Coordinate coordinate) throws InvalidLocationException {
        boolean won = false;
        int x = coordinate.getX();
        int y = coordinate.getY();
        if (coordinate.getX() >= SIZE || coordinate.getY() >= SIZE || null != grid[x][y]) {
            throw new InvalidLocationException();
        }
        grid[x][y] = player.getPawn();
        occupiedCount += 1;
        if (win(player.getPawn().getColor(), coordinate)) {
            won = true;
        }
        return won;
    }

    private boolean win(Colors color, Coordinate coordinate) {
        int firstDirectionCount;
        int secondDirectionCount;
        // 左右找
        firstDirectionCount = walk(color, coordinate, Directions.RIGHT);
        secondDirectionCount = walk(color, coordinate, Directions.LEFT);
        boolean wonHorizontal = (firstDirectionCount + secondDirectionCount + 1) >= 5;
        // 上下找
        firstDirectionCount = walk(color, coordinate, Directions.TOP);
        secondDirectionCount = walk(color, coordinate, Directions.BOTTOM);
        boolean wonVertical = (firstDirectionCount + secondDirectionCount + 1) >= 5;
        // 左上到右下找
        firstDirectionCount = walk(color, coordinate, Directions.TOP_LEFT);
        secondDirectionCount = walk(color, coordinate, Directions.BOTTOM_RIGHT);
        boolean wonDiagonal1 = (firstDirectionCount + secondDirectionCount + 1) >= 5;
        // 右上到左下找
        firstDirectionCount = walk(color, coordinate, Directions.TOP_RIGHT);
        secondDirectionCount = walk(color, coordinate, Directions.BOTTOM_LEFT);
        boolean wonDiagonal2 = (firstDirectionCount + secondDirectionCount + 1) >= 5;
        return (wonHorizontal || wonVertical || wonDiagonal1 || wonDiagonal2);
    }

    private int walk(Colors color, Coordinate coordinate, Directions direction) {
        final int X = coordinate.getX();
        final int Y = coordinate.getY();
        Colors neighborsColor;
        int pawnCount = 0;
        int walkX;
        int walkY;
        Pawn pawn;
        switch (direction) {
            case RIGHT:
                walkX = X + 1;
                if (walkX < SIZE) {
                    pawn = grid[walkX][Y];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkX < SIZE && neighborsColor == color) {
                        pawn = grid[walkX][Y];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkX += 1;
                    }
                }
                break;
            case LEFT:
                walkX = X - 1;
                if (walkX >= 0) {
                    pawn = grid[walkX][Y];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkX >= 0 && neighborsColor == color) {
                        pawn = grid[walkX][Y];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkX -= 1;
                    }
                }
                break;
            case TOP:
                walkY = Y - 1;
                if (walkY >= 0) {
                    pawn = grid[X][walkY];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkY >= 0 && neighborsColor == color) {
                        pawn = grid[X][walkY];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkY -= 1;
                    }
                }
                break;
            case BOTTOM:
                walkY = Y + 1;
                if (walkY < SIZE) {
                    pawn = grid[X][walkY];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkY >= 0 && neighborsColor == color) {
                        pawn = grid[X][walkY];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkY += 1;
                    }
                }
                break;
            case TOP_RIGHT:
                walkX = X + 1;
                walkY = Y - 1;
                if (walkX < SIZE && walkY >= 0) {
                    pawn = grid[walkX][walkY];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkX < SIZE && walkY >= 0 && neighborsColor == color) {
                        pawn = grid[walkX][walkY];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkX += 1;
                        walkY -= 1;
                    }
                }
                break;
            case BOTTOM_RIGHT:
                walkX = X + 1;
                walkY = Y + 1;
                if (walkX < SIZE && walkY < SIZE) {
                    pawn = grid[walkX][walkY];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkX < SIZE && walkY < SIZE && neighborsColor == color) {
                        pawn = grid[walkX][walkY];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkX += 1;
                        walkY += 1;
                    }
                }
                break;
            case BOTTOM_LEFT:
                walkX = X - 1;
                walkY = Y + 1;
                if (walkX >= 0 && walkY < SIZE) {
                    pawn = grid[walkX][walkY];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkX >= 0 && walkY < SIZE && neighborsColor == color) {
                        pawn = grid[walkX][walkY];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkX -= 1;
                        walkY += 1;
                    }
                }
                break;
            case TOP_LEFT:
                walkX = X - 1;
                walkY = Y - 1;
                if (walkX >= 0 && walkY >= 0) {
                    pawn = grid[walkX][walkY];
                    neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                    while (null != pawn && walkX >= 0 && walkY >= 0 && neighborsColor == color) {
                        pawn = grid[walkX][walkY];
                        neighborsColor = (null == pawn) ? Colors.NONE : pawn.getColor();
                        if(neighborsColor == color) {
                            pawnCount += 1;
                        }
                        walkX -= 1;
                        walkY -= 1;
                    }
                }
                break;
            default:
                System.out.println("错误的方向。");
        }
        return pawnCount;
    }
}
